package com.openapi.protocol;

import com.alibaba.fastjson.JSONObject;
import com.openapi.common.WXUtil;
import com.openapi.common.entity.ServerHeart;
import com.openapi.config.RedisConfig;
import com.openapi.config.VedioAdressMapConfig;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;

public class ServerHandler extends ChannelInboundHandlerAdapter {
    private static final Logger log = LoggerFactory.getLogger(ServerHandler.class);
    private final String serviceAPI;
    private final RedisConfig redisConfig;

    public static final String  MSG_FIXED_LABEL                        = "##CMD^"; //固定标签##CMD^
    public static final int MSG_DEVICENOTICE                       = 1; //设备通知消息
    public static final int MSG_SERVERREGIST                       = 2; //流媒体服务器注册通知消息
    public static final int MSG_MEDIACTRL                          = 3; //流媒体控制
    public static final int MSG_DEVICEINFOLIST                     = 4; //返回设备信息列表
    public static final int MSG_GETDEVICELIST                      = 5; //取设备信息列表
    public static final int MSG_PUSHSTREAMNOTICE                   = 7; //推流开始通知，返回推流访问地址
    public static final int MSG_UPLOADRESULT                       = 9; //上传结果通知，返回java端历史视频上传结果
    public static final int MSG_HEART_BEAT_PACKET                  = 10;//心跳

    public ServerHandler( String serviceAPI,RedisConfig redisConfig) {
        this.serviceAPI = serviceAPI;
        this.redisConfig = redisConfig;
    }

    public static String getServiceName() throws Exception {
        Iterator<String> iter = Server.map.keySet().iterator();
        while(iter.hasNext()) {
            String key = iter.next();
            if(!key.isEmpty()){
                return key;
            }
        }
        return "";
    }

    //异步发送
    public static int sendMsg(String dut, String data) throws Exception {
        ChannelHandlerContext ctx = Server.map.get(dut);
        if( null == ctx ){
            log.error(" 无法找到该设备 设备编号： " +  dut);
            return 1;
        }
        String strlen = String.format("%08x", data.getBytes("UTF-8").length);
        String packageData = MSG_FIXED_LABEL+ strlen + data;

        ChannelFuture future  = ctx.writeAndFlush(Unpooled.copiedBuffer(packageData, CharsetUtil.UTF_8));
        future.await();
        if(future.isSuccess()) {
            return 0;
        } else {
            log.error(" 武汉依迅发送给设备： " +  dut + "失败" + future);
            return 1;
        }
    }

    /*
     * channelAction
     * channel 通道 action 活跃的
     * 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress().toString() + " 通道已激活！");
    }

    /*
     * channelInactive
     * channel 通道 Inactive 不活跃的
     * 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info(ctx.channel().remoteAddress().toString() + " 通道不活跃！");
        // 关闭流
    }

    /**
     * @author Taowd
     * TODO  此处用来处理收到的数据中含有中文的时  出现乱码的问题
     * 2017年8月31日 下午7:57:28
     * @param buf
     * @return
     */
    private String getMessage(ByteBuf buf) {
        byte[] con = new byte[buf.readableBytes()];
        buf.readBytes(con);
        try {
            return new String(con, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 功能：读取服务器发送过来的信息
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            // 第一种：接收字符串时的处理
            ByteBuf buf = (ByteBuf) msg;
            String data = getMessage(buf);
            log.info(" buf：" + data);
            if (data.length() < 1) return;
            String jsondata = data.substring(4, data.length() -1);
            log.info(" jsondata：" + jsondata);
            JSONObject object = JSONObject.parseObject(jsondata);
            int msgtype = Integer.parseInt(object.get("msgtype").toString());
            JSONObject body = JSONObject.parseObject(object.get("body").toString());
            String servername = "";
            if (MSG_DEVICENOTICE == msgtype || MSG_SERVERREGIST == msgtype ||
                    MSG_UPLOADRESULT == msgtype || MSG_PUSHSTREAMNOTICE == msgtype ||
                    MSG_HEART_BEAT_PACKET == msgtype) {
                log.info(" msgtype：" + msgtype + " servername: " + body.get("servername").toString());
                servername = body.get("servername").toString();
                if (Server.map.get(servername) == null || !Server.map.get(servername).equals(ctx)) {
                    Server.map.put(servername, ctx);
                }
            }

                    switch (msgtype) {
                        case MSG_DEVICENOTICE://1; //设备通知消息
                            LinkedHashMap<String, String> devMsgMap = new LinkedHashMap<>();
                            devMsgMap.put("devid", body.get("devid").toString());
                            devMsgMap.put("channel", Integer.parseInt(body.get("channel").toString()) + "");
                            devMsgMap.put("action", body.get("action").toString());
                            devMsgMap.put("streamtype", Integer.parseInt(body.get("streamtype").toString()) + "");
                            redisConfig.redisTemplate().opsForHash().put("DeviceMsg",devMsgMap.get("devid") + devMsgMap.get("channel"),devMsgMap);

                            String devMsgUrl = serviceAPI + "/video/devOnlineMsg";
                            String responseJsonMsg = WXUtil.networkRequest(devMsgUrl, "POST", WXUtil.mapToString(devMsgMap), "application/json");
                            log.info(" 设备通知消息 msgtype：" + msgtype + " servername: " + body.get("servername").toString()+ responseJsonMsg + " 请求数据: " + WXUtil.mapToString(devMsgMap));
                            break;
                        case MSG_SERVERREGIST://2; //流媒体服务器注册通知消息
                            log.info(" 流媒体服务器注册通知消息 msgtype：" + msgtype + " servername: " + body.get("servername").toString());
                            break;
                        case MSG_PUSHSTREAMNOTICE://7; //推流开始通知，返回推流访问地址
                        {
                            LinkedHashMap<String, String> resultMap = new LinkedHashMap<>();
                            String mapHttpurl = getMapUrl(body);
                            resultMap.put("devid", body.get("devid").toString());
                            resultMap.put("streamtype", Integer.parseInt(body.get("streamtype").toString()) + "");
                            resultMap.put("httpurl", mapHttpurl);
                            resultMap.put("rtmpurl", body.get("rtmpurl").toString());
                            resultMap.put("channel", Integer.parseInt(body.get("channel").toString()) + "");
                            resultMap.put("remoteAddress", body.get("servername").toString());
                            redisConfig.redisTemplate().opsForHash().put("PushStream",resultMap.get("devid") + resultMap.get("channel"),resultMap);
                    String allUrl = serviceAPI + "/video/mediaStorageDetail";
                    String responseJson = WXUtil.networkRequest(allUrl, "POST", WXUtil.mapToString(resultMap), "application/json");
                    log.info("保存实时视频信息结果：" + responseJson + " 请求数据: " + WXUtil.mapToString(resultMap));
                    break;
                }
                case MSG_UPLOADRESULT://9; //上传结果通知，返回java端历史视频上传结果
                {
                    LinkedHashMap<String, String> mediaListMap = new LinkedHashMap<>();
                    String mapHttpurl = getMapUrl(body);
                    mediaListMap.put("devid", body.get("devid").toString());
                    mediaListMap.put("streamtype", body.get("streamtype").toString());
                    mediaListMap.put("channel", body.get("channel").toString());
                    mediaListMap.put("tbegin", body.get("tbegin").toString());
                    mediaListMap.put("tend", body.get("tend").toString());
                    mediaListMap.put("errorcode", body.get("errorcode").toString());
                    mediaListMap.put("serverAddress", body.get("servername").toString());
                    mediaListMap.put("httpurl", mapHttpurl);
                    mediaListMap.put("rtmpurl", body.get("rtmpurl").toString());
                    WXUtil.mapToString(mediaListMap);

                    String mediaListUrl = serviceAPI + "/video/mediaListResultInfo";
                    String responseJson = WXUtil.networkRequest(mediaListUrl, "POST", WXUtil.mapToString(mediaListMap), "application/x-www-form-urlencoded");
                    log.info("保存历史视频信息结果：" + responseJson + " 请求数据: " + WXUtil.mapToString(mediaListMap));
                    break;
                }
                case MSG_HEART_BEAT_PACKET://10;//心跳
                {
                    ServerHeart heart = new ServerHeart();
                    heart.servername = body.get("servername").toString();
                    heart.connected = body.get("connected").toString();
                    heart.serverport = body.get("serverport").toString();
                    redisConfig.redisTemplate().opsForHash().put("HeartBeatInfo1077",heart.servername,heart);
                    log.info("心跳 msgtype：" + msgtype + " servername: " + body.get("servername").toString());
                    break;
                }
                default:
                    break;

            }

           ReferenceCountUtil.release(data);
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }



    private String getMapUrl(JSONObject body)
    {
        String httpurl = body.get("httpurl").toString();
        String[] tmps = httpurl.split("/");
        String adress = tmps[2];
        List<String> varList = VedioAdressMapConfig.getOriHttpAddressBean();
        int oriIndex = varList.indexOf(adress);
        if (oriIndex < 0) return  httpurl;
        String mapAddress = VedioAdressMapConfig.getMapHttpAddressBean().get(oriIndex);
        String mapHttpurl = httpurl.replaceAll(adress,mapAddress);
        return  mapHttpurl;
    }

    private int getIndex(List<String> oriAddress,String tmp)
    {
        for (int i= 0; i < oriAddress.size(); i ++)
        {
            if (tmp.contains(oriAddress.get(i)))
            {
                return  i;
            }
        }
        return  -1;
    }


    /**
     * 功能：读取完毕客户端发送过来的数据之后的操作
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("服务端接收数据完毕..");
        // 第一种方法：写一个空的buf，并刷新写出区域。完成后关闭sock channel连接。
//        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

    /**
     * 功能：服务端发生异常的操作
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        log.info("异常信息：\r\n" + cause.getMessage());
        cause.printStackTrace();
    }
}